﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

using ArcannEngine.Entities;
using ArcannEngine.Render;

namespace ArcannEngine.Components
{
    public class CameraComponent : Component
    {
        // ///////////////////////////////////////////
        //                KAMERA
        // ///////////////////////////////////////////
        //
        // OBSŁUGA:
        // zmiana widoku (izom/fpp) - C
        // zwiększ czułość - +
        // zmniejsz czułość - -
        // 
        // W KAMERZE IZOMETRYCZNEJ:
        // z-- - I
        // z++ - K
        // y++ - U
        // y-- - O
        // x-- - L
        // x++ - J
        //
        // NA RAZIE TYMCZASOWO, MUSZE POPRAWIC!!!
        // ZEBY DZIALALO ODKOMENTOWALEM LINIJKI ODPOWIEDZIALNE ZA OBROT W STEROWANIU
        // Q / E - obrot kamery wokol gostka
        //
        // W FPP:
        // przód - W
        // tył - S
        // obrót w prawo - D
        // obrót w lewo - A
        // do góry - E
        // w dół - Q
        //
        // //////////////////////////////////////////////

        float delta_wsp = 0.5f;  // delta współczynnika czułości ruchu (dodawana do wsp po wciśnięciu +, odejmowana przy -)
        float wsp = 1;  // współczynnik ruchu, mnożony przez wszystkie zmienne w fpp
        float camAngle;
        public float zoom;
        public float zoomMax = 10000f;
        public float zoomMin = 500f;
        float camHeight;
        bool camera;  // true - kamera izometryczna, false - fpp

        Entity ParentEntity;
        KeyboardState keyState;
        KeyboardState oldKeyState;
        MouseState mouse;
        int scrollCount;
        GamePadState gamePadState;
        private float aspectRatio;
        private List<HeightMapInfo> heightInfos;

        public Vector3 camera_position;  // pierwszy wektor w LookAt
        public Vector3 target;  // drugi wektor w LookAt
        public Vector3 cameraDirection; // wektor mowiacy o kierunku "patrzenia" kamery

        // zmienne dotyczące przekształceń kamery
        private Vector3 lxyz;
        private Vector3 camera_movement;
        private Vector3 camera_bufor;
        double angle = 0.0, deltaAngle = 0.02;
        float yy = 0;

        public float AspectRatio
        {
            get { return aspectRatio; }
        }

        protected override string GetName()
        {
            return "Camera";
        }


        public CameraComponent(Entity ParentEntity)
            : base(ParentEntity)
        {
            Initialize();
            this.ParentEntity = ParentEntity;
            zoom = 5000;
            scrollCount = 0;
            camHeight = 6000;

            camera = true;
            target = ParentEntity.position;
            camAngle = 200;

            camera_movement = new Vector3(0, 100, 0);
            camera_bufor = camera_movement;
            lxyz = new Vector3(0, 0, -1);

            this.heightInfos = ParentEntity.Manager.heightMaps;
        }

        public void changeTarget(Entity entity)
        {
            //Parent = entity;
        }

        public override void Update(GameTime gameTime)
        {
            mouse = Mouse.GetState();
            keyState = Keyboard.GetState();
            gamePadState = GamePad.GetState(PlayerIndex.One);

            if (mouse.ScrollWheelValue / 120 != scrollCount)
            {
                zoom += 50 * (scrollCount - mouse.ScrollWheelValue / 120);

                if (zoom < zoomMin) zoom = zoomMin;
                else if (zoom > zoomMax) zoom = zoomMax;
                else camHeight += 20 * (scrollCount - mouse.ScrollWheelValue / 120);

                scrollCount = mouse.ScrollWheelValue / 120;

            }

            if (keyState.IsKeyDown(Keys.E) && camera)
            {
                camAngle += 2.0f;
                if (camAngle > 360f) camAngle = 0f;
            }

            if (keyState.IsKeyDown(Keys.Q) && camera)
            {
                camAngle -= 2.0f;
                if (camAngle < 0f) camAngle = 360f;
            }

            if (/*&& this.gamePadState.IsButtonDown(Buttons.LeftShoulder) &&*/ camera)
            {
                camAngle += this.gamePadState.ThumbSticks.Right.X * 1.0f;
                if (camAngle > 360f) camAngle = 0f;
                if (camAngle < 0f) camAngle = 360f;
            }

            /* if (mouse.X > Parent.SceneManager.Game.GraphicsDevice.Viewport.Width * 0.5f + 200)
             {
                 camAngle += 2.0f;
                 if (camAngle > 360f) camAngle = 0f;
             }

             if (mouse.X < Parent.SceneManager.Game.GraphicsDevice.Viewport.Width * 0.5f - 200)
             {
                 camAngle -= 2.0f;
                 if (camAngle < 0f) camAngle = 360f;
             }
             */
            if (keyState.IsKeyDown(Keys.C) && oldKeyState.IsKeyUp(Keys.C))
            {
                camera = !camera;
            }

            if (keyState.IsKeyDown(Keys.R))
            {
                zoom = 900;
                camHeight = 500;
            }

            if (keyState.IsKeyDown(Keys.OemPlus)) wsp += delta_wsp;
            if (keyState.IsKeyDown(Keys.OemMinus)) wsp -= delta_wsp;

            if (camera == true)
            {
                camera_izo();
                UpdateAngle();
            }
            if (camera == false)
            {
                camera_fpp();
            }
            oldKeyState = keyState;
        }

        public void UpdateAngle()
        {
            camera_position.X = target.X + zoom * (float)Math.Cos(MathHelper.ToRadians(camAngle));
            camera_position.Z = target.Z + zoom * (float)Math.Sin(MathHelper.ToRadians(camAngle));
            //if (camHeight - 1500 < target.Y) camHeight = target.Y + 1500;
            camera_position.Y += camHeight;

            bool found = false;
            int i = 0;
            while (!found && i < heightInfos.Count)
            {
                if (heightInfos[i].IsOnHeightmap(camera_position) && camera_position.Y - 300 < heightInfos[i].GetHeight(camera_position))
                {
                    camera_position.Y = heightInfos[i].GetHeight(camera_position) + 300;
                    found = true;
                }
                i++;
            }

            cameraDirection.X = target.X - camera_position.X;
            cameraDirection.Z = target.Z - camera_position.Z;
            cameraDirection.Normalize();
            EngineCommon.camDirection = cameraDirection;
        }


        protected override void Initialize()
        {
            aspectRatio = this.Parent.Manager.Game.Graphics.GraphicsDevice.Viewport.AspectRatio;
            base.Initialize();
        }

        private void camera_izo()
        {

            target = ParentEntity.position;
            camera_position = target;
            target.Y += 100 + (50000 / zoom);
            /*if (keyState.IsKeyDown(Keys.R))
             {
                 camera_position.X = (float)(camera_position.X * Math.Cos(wsp / 5)) - (float)(camera_position.Y * Math.Sin(wsp / 5)) + camera_position.X * 0;
                 camera_position.Y = (float)(camera_movement.Y * Math.Cos(wsp / 5)) + (float)(camera_position.X * Math.Sin(wsp / 5)) + camera_position.X * 0;
                 camera_position.X = camera_position.X * 1 + (float)(camera_position.Y * 0) + camera_position.Z * 1;
             }*/

            //tryb obserwatora -zakomentowac zeby byla zwykla kamera izo
            //camera_position *= 0.6f;

            // !!! ZMIANA
            // Odkomentowałem, no bo kamera teraz i tak jest powiazana z graczem
            //if (keyState.IsKeyDown(Keys.I)) camera_position = 3*target/4;
            //if (keyState.IsKeyDown(Keys.I)) camera_movement.Z-=15*wsp;
            //if (keyState.IsKeyDown(Keys.K)) camera_movement.Z+=15*wsp;
            //if (keyState.IsKeyDown(Keys.U)) camera_movement.Y-=15*wsp;
            //if (keyState.IsKeyDown(Keys.O)) camera_movement.Y+=15*wsp;
            //if (keyState.IsKeyDown(Keys.J)) camera_movement.X-=15*wsp;
            //if (keyState.IsKeyDown(Keys.L)) camera_movement.X+=15*wsp;
            if (keyState.IsKeyDown(Keys.U)) camHeight -= 20 * wsp;
            if (keyState.IsKeyDown(Keys.O)) camHeight += 20 * wsp;
        }

        private void camera_fpp()
        {
            camera_position = camera_bufor;

            camera_position.Y = yy;

            if (keyState.IsKeyDown(Keys.W)) flatMove(200);
            if (keyState.IsKeyDown(Keys.S)) flatMove(-200);
            if (keyState.IsKeyDown(Keys.A))
            {
                angle -= deltaAngle * wsp;
                rotationMove(angle);
            }
            if (keyState.IsKeyDown(Keys.D))
            {
                angle += deltaAngle * wsp;
                rotationMove(angle);
            }
            if (keyState.IsKeyDown(Keys.Q))
            {
                yy -= 3 * wsp;
            }
            if (keyState.IsKeyDown(Keys.E))
            {
                yy += 3 * wsp;
            }
            target = camera_position + lxyz;
            camera_bufor = camera_position;
        }

        private void rotationMove(double ang)
        {
            lxyz.X = (float)Math.Sin(ang) * wsp;
            lxyz.Z = (float)-Math.Cos(ang) * wsp;
        }

        private void flatMove(double i)
        {
            camera_position.X = camera_position.X + (float)i * (lxyz.X) * 0.1f * wsp;
            camera_position.Z = camera_position.Z + (float)i * (lxyz.Z) * 0.1f * wsp;
        }
    }
}
